package bean.managed;

import entity.CustomerInvoiceEntity;
import bean.managed.util.JsfUtil;
import bean.session.CustomerInvoiceEntityFacade;

import bean.session.MonthlyRevenueEntityFacade;
import entity.CustomerAccountEntity;
import entity.CustomerEntity;
import entity.MonthlyRevenueEntity;
import entity.PaymentEntity;
import entity.ReceiptEntity;
import entity.SalesOrderEntity;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.SessionScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
import javax.faces.model.SelectItem;
import javax.servlet.http.HttpServletRequest;
import managedbean.AbstractBacking;
import managedbean.DownloadPDF;
import managedbean.Jasper;
import net.sf.jasperreports.engine.JRException;
import org.primefaces.event.FileUploadEvent;
import type.InternalStatus;
import type.InvoiceStatus;
import type.InvoiceType;
import type.ReceiptStatus;
import util.Mailer;
import util.PDFGenerator;

@ManagedBean(name = "customerInvoiceEntityController")
@SessionScoped
public class CustomerInvoiceEntityController extends AbstractBacking implements Serializable {

    private CustomerInvoiceEntity current;
    private List<CustomerInvoiceEntity> allInvoices;
    private List<CustomerInvoiceEntity> ciaInvoices;
    private List<CustomerInvoiceEntity> normalInvoices;
    private List<CustomerInvoiceEntity> reminderInvoices;
    private List<CustomerInvoiceEntity> unpostedInvoices;
    private List<CustomerInvoiceEntity> unpaidInvoices;
    private Date paymentDate;
    @EJB
    private bean.session.CustomerInvoiceEntityFacade ejbFacade;
    @EJB
    private bean.session.SalesOrderEntityFacade salesOrderFacade;
    @EJB
    private bean.session.PaymentEntityFacade paymentFacade;
    @EJB
    private bean.session.CustomerAccountEntityFacade customerAccountFacade;
    @EJB
    private bean.session.CustomerEntityFacade customerFacade;
    @EJB
    private bean.session.ReceiptEntityFacade receiptFacade;
    @EJB
    private MonthlyRevenueEntityFacade revenueFacade;
    
    @ManagedProperty(value = "#{systemMessageController}")
    private SystemMessageController systemMessageController;
    @ManagedProperty(value = "#{downloadPdf}")
    private DownloadPDF pdfDownloadController;
    @ManagedProperty(value = "#{jasper}")
    private Jasper jasper;

    public CustomerInvoiceEntityController() {
    }

    public Jasper getJasper() {
        return jasper;
    }

    public void setJasper(Jasper jasper) {
        this.jasper = jasper;
    }

    public CustomerInvoiceEntity getSelected() {
        if (current == null) {
            current = new CustomerInvoiceEntity();
        }
        HttpServletRequest request = (HttpServletRequest) super.getFacesContext().getExternalContext().getRequest();
        if (request.getParameter("id") != null) {
            Long id = Long.valueOf(request.getParameter("id"));
            current = ejbFacade.find(id);
        }
        return current;
    }

    private CustomerInvoiceEntityFacade getFacade() {
        return ejbFacade;
    }

    public String prepareList() {
        recreateModel();
        return "/upload/List";
    }

    public String prepareView() {
        return "/entities/customerInvoiceEntity/View.xhtml";
    }

    public String prepareCreate() {
        return "/entities/customerInvoiceEntity/Create";
    }

    public String create() {
        try {
            current.created(getLoginPerson());
            getFacade().create(current);
            JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Bundle").getString("CustomerInvoiceEntityCreated"));
            return prepareCreate();
        } catch (Exception e) {
            JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured"));
            return null;
        }
    }

    public String prepareEdit() {
        return "/entities/customerInvoiceEntity/Edit";
    }

    public String update() {
        try {
            current.modified(getLoginPerson());
            getFacade().edit(current);
            JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Bundle").getString("CustomerInvoiceEntityUpdated"));
            return "/entities/customerInvoiceEntity/View";
        } catch (Exception e) {
            JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured"));
            return null;
        }
    }

    public String destroy() {
        performDestroy();
        recreateModel();
        return "List";
    }

    public String destroyAndView() {
        performDestroy();
        recreateModel();
        // all items were removed - go back to list
        return "List";
    }

    private void performDestroy() {
        try {
            getFacade().remove(current);
            JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Bundle").getString("CustomerInvoiceEntityDeleted"));
        } catch (Exception e) {
            JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured"));
        }
    }

    private void recreateModel() {
        current = null;
    }

    public SelectItem[] getItemsAvailableSelectMany() {
        return JsfUtil.getSelectItems(ejbFacade.findAll(), false);
    }

    public SelectItem[] getItemsAvailableSelectOne() {
        return JsfUtil.getSelectItems(ejbFacade.findAll(), true);
    }

    @FacesConverter(forClass = CustomerInvoiceEntity.class)
    public static class CustomerInvoiceEntityControllerConverter implements Converter {

        public Object getAsObject(FacesContext facesContext, UIComponent component, String value) {
            if (value == null || value.length() == 0) {
                return null;
            }
            CustomerInvoiceEntityController controller = (CustomerInvoiceEntityController) facesContext.getApplication().getELResolver().
                    getValue(facesContext.getELContext(), null, "customerInvoiceEntityController");
            return controller.ejbFacade.find(getKey(value));
        }

        java.lang.Long getKey(String value) {
            java.lang.Long key;
            key = Long.valueOf(value);
            return key;
        }

        String getStringKey(java.lang.Long value) {
            StringBuffer sb = new StringBuffer();
            sb.append(value);
            return sb.toString();
        }

        public String getAsString(FacesContext facesContext, UIComponent component, Object object) {
            if (object == null) {
                return null;
            }
            if (object instanceof CustomerInvoiceEntity) {
                CustomerInvoiceEntity o = (CustomerInvoiceEntity) object;
                return getStringKey(o.getId());
            } else {
                throw new IllegalArgumentException("object " + object + " is of type " + object.getClass().getName() + "; expected type: " + CustomerInvoiceEntityController.class.getName());
            }
        }
    }

    public CustomerInvoiceEntity getCurrent() {
        return current;
    }

    public void setCurrent(CustomerInvoiceEntity current) {
        System.out.println(getClass().getName() + ": setting current: " + current);
        this.current = current;
    }

    public List<CustomerInvoiceEntity> getAllInvoices() {
        allInvoices = ejbFacade.findAll();
        return allInvoices;
    }

    public void setAllInvoices(List<CustomerInvoiceEntity> allInvoices) {
        this.allInvoices = allInvoices;
    }

    public SystemMessageController getSystemMessageController() {
        return systemMessageController;
    }

    public void setSystemMessageController(SystemMessageController systemMessageController) {
        this.systemMessageController = systemMessageController;
    }

    public String exportToPDF() {
        String path = PDFGenerator.getFilePdfFolderPath(current.getId());
        File file = new File(path);
        boolean exists = file.exists();
        System.out.println("Exist: " + exists + path);
        if (!exists) {
            HttpServletRequest request = (HttpServletRequest) super.getFacesContext().getExternalContext().getRequest();
            path = PDFGenerator.generatePDFFromUrl(request, "ViewInvoiceNoFrame.xhtml", current.getId(), null);
        }
        pdfDownloadController.setPath(path);
        return path;
    }

    public String postInvoiceViaEmailAndRedirectToView() {
        postInvoiceViaEmail();
        return "/entities/customerInvoiceEntity/View.xhtml";
    }

    public void postInvoiceViaEmail() {
        System.out.println("PostInvoiceViaEmail!");
        try {

            String path = exportToPDF();
            Mailer.doSend("staff.merlion@gmail.com",
                    current.getCustomerAccount().getCustomer().getContact().getEmail(),
                    "", "New Invoice By Merlion Food", "As Attached", path, null);

            if (!current.isToBePosted()) {
                return;
            }
            postInvoice();
        } catch (Exception ex) {
            JsfUtil.addErrorMessage(ex, ex.getMessage());
            ex.printStackTrace();
        }
    }

    public void postInvoice() {
        System.out.println("in postInvoice()");
        if (!current.isToBePosted()) {
            return;
        }

        current.setStatus(InvoiceStatus.posted);
        update();

        SalesOrderEntity salesOrder = current.getBillingRequest().getSalesOrder();
        if (salesOrder.getInternalStatus() == InternalStatus.CIABillingConfirmPending) {
            salesOrder.setInternalStatus(InternalStatus.CIAPaymentPending);
        } else if (salesOrder.getInternalStatus() == InternalStatus.BillingConfirmaPending) {
            salesOrder.setInternalStatus(InternalStatus.PaymentPending);
        }
        salesOrder.modified(getLoginPerson());
        salesOrderFacade.edit(salesOrder);

        JsfUtil.addSuccessMessage("Invoice #" + current.getId() + " posted!");
    }

    public String recordPaymentAndView() {
        recordPayment();
        return "/entities/customerInvoiceEntity/View.xhtml";
    }

    public void recordPayment() {
        fakeHandleProofOfPaymentUpload();

        if (current.getPayment() == null) {
            System.err.println("Proof of payment is not yet uploaded!");
            return;
        }
        PaymentEntity payment = current.getPayment();
        //record late
        if (current.getDueDate() != null) {
            Date today = new Date();
            payment.setLate(today.after(current.getDueDate()));
        }

        payment.created(getLoginPerson());
        payment.setCreatedAt(paymentDate);
        paymentFacade.create(payment);

        current.setStatus(InvoiceStatus.paid);
        update();

        CustomerAccountEntity customerAccount = current.getCustomerAccount();
        SalesOrderEntity so = current.getBillingRequest().getSalesOrder();

        if (current.getType() == InvoiceType.cashInAdvance) {
            //increase cash balance
            double cashBalance = customerAccount.getCashBalance();
            cashBalance += current.getAmount();
            customerAccount.setCashBalance(cashBalance);

            //update sales order internal status
            so.setInternalStatus(InternalStatus.ProvideLeadTimePending);
            so.modified(getLoginPerson());
            salesOrderFacade.edit(so);

            //inform account executive of the update
            String message = "Customer " + customerAccount.getCustomer().getFullname()
                    + "'s Sales Order #" + so.getId() + " Cash-in-Advance Payment is cleared.";
            systemMessageController.getEjbFacade().createAndSendSystemMessage(so.getSystemUser(),
                    message, "/entities/salesOrderEntity/List.xhtml", systemMessageController);

        } else {
            //increase credit balance
            double creditBalance = customerAccount.getCreditBalance();
            double toCreditBack = current.getAmount();
            if (current.getType() == InvoiceType.reminder) {
                toCreditBack -= current.getLatePenalty();
            }
            creditBalance += toCreditBack;
            customerAccount.setCreditBalance(creditBalance);

            //late payment
            if (current.getType() == InvoiceType.reminder) {
                CustomerEntity customer = customerAccount.getCustomer();
                List<CustomerInvoiceEntity> unpaidReminder = ejbFacade.getUnpaidRemindersForCustomer(customer);
                if (unpaidReminder.isEmpty()) {
                    //unblacklist customer
                    customer.setBlackListed(false);
                    customer.modified(getLoginPerson());
                    customerFacade.edit(customer);

                    //unsuspend currently suspended sales orders
                    List<SalesOrderEntity> saleOrders = salesOrderFacade.getSuspendedSalesOrderForCustomer(customer.getId());
                    String salesOrderIds = "";
                    for (SalesOrderEntity salesOrder : saleOrders) {
                        salesOrder.setStatus(so.getBackupStatus());
                        salesOrder.setBackupStatus(null);

                        salesOrder.modified(getLoginPerson());
                        salesOrderFacade.edit(salesOrder);
                        salesOrderIds += salesOrder.getId() + ", ";
                    }

                    //inform sales about blacklist and suspension
                    String unblackListNotice = "Customer #" + customer.getId() + " "
                            + customer.getFullname() + " has been unblacklisted. ";
                    if (!salesOrderIds.isEmpty()) {
                        unblackListNotice += "Unsuspended sales order(s): " + salesOrderIds.substring(0, salesOrderIds.length() - 2);
                    }
                    systemMessageController.getEjbFacade().createAndSendSystemMessage(
                            customer.getCustomerExecutive(), unblackListNotice,
                            "/entities/salesOrderEntity/List.xhtml",
                            systemMessageController);
                }

            }

            //check if sales order is fully paid
            if (!so.hasOutstandingInvoice()) {
                //update sales order internal status
                so.setInternalStatus(InternalStatus.PaymentComplete);
                so.modified(getLoginPerson());
                salesOrderFacade.edit(so);

                //inform sales about completion of sales order
                String completion = "Sales order #" + so.getId() + " is fully paid.";

                systemMessageController.getEjbFacade().createAndSendSystemMessage(
                        customerAccount.getCustomer().getCustomerExecutive(), completion,
                        "/entities/salesOrderEntity/List.xhtml",
                        systemMessageController);
            }

        }

        customerAccount.modified(getLoginPerson());
        customerAccountFacade.edit(customerAccount);

        generateReceipt();

        paymentDate = null;
    }

    public void fakeHandleProofOfPaymentUpload() {
        PaymentEntity payment = new PaymentEntity();
        if (current.getPayment() != null) {
            System.err.println("Invoice #" + current.getId() + " already paid!");
            return;
        }
        payment.setProofOfPaymentFilePath("");

        current.setPayment(payment);
        payment.setInvoice(current);
    }

    public void handleProofOfPaymentUpload(FileUploadEvent event) {
        System.out.println("uploading... " + event.getFile().getFileName());
        PaymentEntity payment = new PaymentEntity();
        if (current.getPayment() != null) {
            System.err.println("Invoice #" + current.getId() + " already paid!");
            return;
        }

        payment.setProofOfPaymentFilePath(event.getFile().getFileName());

        current.setPayment(payment);
        payment.setInvoice(current);
        System.out.println("end of record payment");
    }

    public void doNothing() {
        System.out.println("in doNothing.. current invoice: " + current);
    }

    public Date getPaymentDate() {
        return paymentDate;
    }

    public void setPaymentDate(Date paymentDate) {
        this.paymentDate = paymentDate;
    }

    public String exportReceiptToPDF() {

        String path = PDFGenerator.getFilePdfFolderPath(current.getReceipt().getId());
        File file = new File(path);
        boolean exists = file.exists();
        if (!exists) {
            HttpServletRequest request = (HttpServletRequest) super.getFacesContext().getExternalContext().getRequest();
            path = PDFGenerator.generatePDFFromUrl(request, "ViewReceiptNoFrame.xhtml", current.getId(), current.getReceipt().getId());
        }
        pdfDownloadController.setPath(path);
        return path;

    }

    public ReceiptEntity generateReceipt() {
        ReceiptEntity r = current.getReceipt();
        if (current.isToBeReceipt()) {

            ReceiptEntity receipt = new ReceiptEntity();
            receipt.setInvoice(current);
            receipt.setStatus(ReceiptStatus.draft);
            receipt.created(getLoginPerson());
            receiptFacade.create(receipt);

            current.setReceipt(receipt);
            update();

            r = receipt;
        }

        return r;
    }

    public void postReceiptViaEmail() {
        try {
            generateReceipt();
            String path = exportReceiptToPDF();
            Mailer.doSend("staff.merlion@gmail.com",
                    current.getCustomerAccount().getCustomer().getContact().getEmail(),
                    "", "New Receipt By Merlion Food", "As Attached", path, null);

            ReceiptEntity receipt = current.getReceipt();
            receipt.setStatus(ReceiptStatus.posted);
            receipt.modified(getLoginPerson());
            receiptFacade.edit(receipt);

            JsfUtil.addSuccessMessage("Receipt #" + current.getReceipt().getId() + " posted!");

        } catch (Exception ex) {
            JsfUtil.addErrorMessage(ex, ex.getMessage());
            ex.printStackTrace();
        }
    }

    public void markReceiptPosted() {
        generateReceipt();

        ReceiptEntity receipt = current.getReceipt();
        receipt.setStatus(ReceiptStatus.posted);
        receipt.modified(getLoginPerson());
        receiptFacade.edit(receipt);

        JsfUtil.addSuccessMessage("Receipt #" + current.getReceipt().getId() + " posted!");
    }

    public List<CustomerInvoiceEntity> getCiaInvoices() {
        ciaInvoices = ejbFacade.getAllCIA();
        return ciaInvoices;
    }

    public void setCiaInvoices(List<CustomerInvoiceEntity> ciaInvoices) {
        this.ciaInvoices = ciaInvoices;
    }

    public List<CustomerInvoiceEntity> getNormalInvoices() {
        normalInvoices = ejbFacade.getAllNormal();
        return normalInvoices;
    }

    public void setNormalInvoices(List<CustomerInvoiceEntity> normalInvoices) {
        this.normalInvoices = normalInvoices;
    }

    public List<CustomerInvoiceEntity> getReminderInvoices() {
        reminderInvoices = ejbFacade.getAllReminder();
        return reminderInvoices;
    }

    public void setReminderInvoices(List<CustomerInvoiceEntity> reminderInvoices) {
        this.reminderInvoices = reminderInvoices;
    }

    public List<CustomerInvoiceEntity> getUnpostedInvoices() {
        unpostedInvoices = ejbFacade.getAllUnposted();
        return unpostedInvoices;
    }

    public void setUnpostedInvoices(List<CustomerInvoiceEntity> unpostedInvoices) {
        this.unpostedInvoices = unpostedInvoices;
    }

    public List<CustomerInvoiceEntity> getUnpaidInvoices() {
        unpaidInvoices = ejbFacade.getAllUnpaid();
        return unpaidInvoices;
    }

    public void setUnpaidInvoices(List<CustomerInvoiceEntity> unpaidInvoices) {
        this.unpaidInvoices = unpaidInvoices;
    }

    public DownloadPDF getPdfDownloadController() {
        return pdfDownloadController;
    }

    public void setPdfDownloadController(DownloadPDF pdfDownloadController) {
        this.pdfDownloadController = pdfDownloadController;
    }

    public void generateRevenueReport() {
        generateRevenueReportForYear(2011);
    }
    
    public void generateRevenueReportForYear(int year) {
        System.out.println("generating revenue report.");
        revenueFacade.clearRecordForYear(year);
        ejbFacade.calculateMonthlyRevenueForYear(year);
        try {
            this.getJasper().generateFinanceReport();
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(CustomerInvoiceEntityController.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SQLException ex) {
            Logger.getLogger(CustomerInvoiceEntityController.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(CustomerInvoiceEntityController.class.getName()).log(Level.SEVERE, null, ex);
        } catch (JRException ex) {
            Logger.getLogger(CustomerInvoiceEntityController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}
